#include "coaptask.h"

CoapTask::CoapTask(std::string name, Scheduler* s, CoapTaskSM* sm) 
	: PTask(name, s, sm),
  		up(this),
    	peer(this, &down),
    	down(this, &peer),
    	timer (this, OTime (RESPONSE_TIMEOUT))
{
	STATIC_SYMBOL(up, this);
	STATIC_SYMBOL(down, this);
	STATIC_SYMBOL(peer, this);
  
  	//random messageid initialization
	srand(time(NULL));
	messageid = rand();
}

CoapTask::~CoapTask()
{
	DELETE_SYMBOL(&up);
	DELETE_SYMBOL(&down);
	DELETE_SYMBOL(&peer);
}


//********************* Helper functions *********************
Uint16 CoapTask::getNewMessageID()
{
	//TODO - what about better type of generation?
	//TODO - The same Message ID MUST NOT be re-used within the potential retransmission window, calculated as
	//       RESPONSE_TIMEOUT * (2 ^ MAX_RETRANSMIT - 1) plus the expected maximum round trip time.
	return messageid++;
}

void CoapTask::receiveGET(Message* msg)
{
	CoapPeer::GET* peer_get= (CoapPeer::GET *) msg;

	CoapSAP::Getreqin* up_getreqin= (CoapSAP::Getreqin*) up.create(CoapSAP::dtreqin);
	up_getreqin->messageID= peer_get->messageID;
	up_getreqin->src= peer_get->addr;
	up_getreqin->uripath= peer_get->uripath;

	up.putMessage(up_getreqin);
}


//********************* Idle state *********************

bool CoapTask::idle_Getreq(Message* msg)
{
	CoapSAP::Getreqout* getmsg= (CoapSAP::Getreqout*) msg;
	//create message
	CoapPeer::GET* peer_get = (CoapPeer::GET*) peer.create(CoapPeer::get);
	//control
	if(!peer_get) return false;
	//fill message
	peer_get->messageID= getNewMessageID();
	peer_get->uripath= getmsg->uripath;
	peer_get->addr= getmsg->dest;
	//send message
	peer.putMessage(peer_get);
	//TODO - save peer_get for resend? save type?
	
	//setup next expected messageid 
	expected_messageid= peer_get->messageID;
	state= CoapTaskSM::WfAck;
	timer_retries= 0;
	timer.start();
	return false;
}

bool CoapTask::idle_Delreq(Message* msg)
{
	CoapSAP::Delreqout* delmsg= (CoapSAP::Delreqout*) msg;
	//create message
	CoapPeer::DEL* peer_del = (CoapPeer::DEL*) peer.create(CoapPeer::del);
	//control
	if(!peer_del) return false;
	//fill message
	peer_del->messageID= getNewMessageID();
	peer_del->uripath= delmsg->uripath;
	peer_del->addr= delmsg->dest;
	//send message
	peer.putMessage(peer_del);
	//TODO - save peer_del for resend? save type?
	
	//setup next expected messageid 
	expected_messageid= peer_del->messageID;
	state= CoapTaskSM::WfAck;
	timer_retries= 0;
	timer.start();
	return false;
}

bool CoapTask::idle_Putreq(Message* msg)
{
	CoapSAP::Putreqout* putmsg= (CoapSAP::Putreqout*) msg;
	//create message
	CoapPeer::PUT* peer_put = (CoapPeer::PUT*) peer.create(CoapPeer::put);
	//control
	if(!peer_put) return false;
	//fill message
	peer_put->messageID= getNewMessageID();
	peer_put->uripath= putmsg->uripath;
	peer_put->addr= putmsg->dest;
	peer_put->payload= putmsg->payload;
	//send message
	peer.putMessage(peer_put);
	//TODO - save peer_put for resend? save type?
	
	//setup next expected messageid 
	expected_messageid= peer_put->messageID;
	state= CoapTaskSM::WfAck;
	timer_retries= 0;
	timer.start();
	return false;
}

bool CoapTask::idle_GET(Message* msg)
{
	//TODO - send get up, change state
	return false;
}


bool CoapTask::idle_DEL(Message* msg)
{
	//TODO - send del up, change state
	return false;
}

bool CoapTask::idle_PUT(Message* msg)
{
	//TODO - send put up, change state
	return false;
}

//********************* wfAck state *********************

bool CoapTask::wfAck_ACK(Message* msg)
{
	//send Dtrespin info up
	CoapPeer::ACK* peer_ack = (CoapPeer::ACK*) msg;
	//check message id
	if(expected_messageid == peer_ack->messageID)
	{
		CoapSAP :: Dtrespin* up_dtrespin = (CoapSAP::Dtrespin*) up.create(CoapSAP::dtrespin);
		up_dtrespin->code = peer_ack->code;
		up_dtrespin->payload= peer_ack->payload;
		up_dtrespin->src= peer_ack->addr;
		up.putMessage(up_dtrespin);
		//stop timer
		timer.stop();
		//change state
		state= CoapTaskSM::Idle;
	}
}

bool CoapTask::wfAck_Timeout(Message* msg)
{
	timer_retries++;
	
	if(timer_retries <= MAX_RETRANSMIT)
	{
		//TODO - resend
		timer.start();
	}
	else
	{
		state= CoapTaskSM::Idle;
		//TODO - reset? wtf is that?
	}
}

//********************* wfDtresp state *********************

bool CoapTask::wfDtresp_Dtrespout_pb(Message* msg)
{
	//TODO - send ACK and change state
	return false;
}

bool CoapTask::wfDtresp_Timeout(Message* msg)
{
	//TODO - i don't know what
	return false;
}
